using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using log4net;
using System.Collections.ObjectModel;

namespace PSSymbolStore.Manager.Windows
{
    public partial class MainForm : Form, ITransactionManagerView
    {
        private static readonly Image _lockIcon = Properties.Resources.LockIcon;
        private static readonly Image _blankIcon = new Bitmap(16, 16);

        private readonly TransactionManagerPresenter _presenter;

        public MainForm()
        {
            InitializeComponent();

            _presenter = new TransactionManagerPresenter(this);
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            LogManager.GetLogger(Utilities.Logger).Info("Exiting the application");
            Application.Exit();
        }

        private void openToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var dlg = new ChooseSymbolStore();
            if (dlg.ShowDialog() == DialogResult.OK)
            {
                LogManager.GetLogger(Utilities.Logger).InfoFormat("Setting symbol store location to: {0}", dlg.SymbolStoreLocation);
                _presenter.SymbolStoreLocation = dlg.SymbolStoreLocation;
            }
        }

        #region Deleting

        private void deleteTransactionToolStripMenuItem_Click(object sender, EventArgs e)
        {
            DoDeleteCommand();
        }

        private void buttonDelete_Click(object sender, EventArgs e)
        {
            DoDeleteCommand();
        }

        private void DoDeleteCommand()
        {
            if (!backgroundWorker.IsBusy)
            {
                DeleteSelectedTransactions();
            }

            UpdateButtonState();
        }

        private void DeleteSelectedTransactions()
        {
            // Get the list of selected transactions
            AddTransactionList deleteEntries = GetSelectedEntries();
            if (deleteEntries.Count > 0)
            {
                LogManager.GetLogger(Utilities.Logger).InfoFormat("Attempting to delete {0} transactions", deleteEntries.Count);

                DeleteOperationStarted();
                backgroundWorker.RunWorkerAsync(deleteEntries);
            }
        }

        private void DeleteOperationStarted()
        {
            openToolStripMenuItem.Enabled = false;
            openRecentToolStripMenuItem.Enabled = false;
            buttonRefresh.Enabled = false;
        }

        private void DeleteOperationComplete()
        {
            openToolStripMenuItem.Enabled = true;
            openRecentToolStripMenuItem.Enabled = true;
            buttonRefresh.Enabled = true;

            toolStripProgressBar.Visible = false;
        }

        #endregion Deleting

        #region Locking/Unlocking

        private void lockTransactionsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            DoLockUnlockCommand(_presenter.Lock);
        }

        private void buttonLock_Click(object sender, EventArgs e)
        {
            DoLockUnlockCommand(_presenter.Lock);
        }

        private void buttonUnlock_Click(object sender, EventArgs e)
        {
            DoLockUnlockCommand(_presenter.Unlock);
        }

        private void unlockTransactionsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            DoLockUnlockCommand(_presenter.Unlock);
        }

        private delegate void LockOrUnlockDel(AddTransactionList transactions);

        private void DoLockUnlockCommand(LockOrUnlockDel del)
        {
            AddTransactionList transactions = GetSelectedEntries();
            if (transactions.Count > 0)
            {
                using (new Hourglass())
                {
                    // Update
                    del(transactions);

                    // todo: refresh here? or update the in-memory data automatically?

                    // Invalidate the displayed selected rows
                    int index = transactionDataGridView.Rows.GetFirstRow(DataGridViewElementStates.Displayed | DataGridViewElementStates.Selected);
                    while (index != -1)
                    {
                        transactionDataGridView.InvalidateCell(Locked.Index, index);
                        index = transactionDataGridView.Rows.GetNextRow(index, DataGridViewElementStates.Displayed | DataGridViewElementStates.Selected);
                    }
                }
            }

            UpdateButtonState();
        }

        #endregion Locking/Unlocking

        private AddTransactionList GetSelectedEntries()
        {
            var transactions = new AddTransactionList();

            try
            {
                DataGridViewSelectedRowCollection selectedRows = transactionDataGridView.SelectedRows;
                if (selectedRows.Count > 0)
                {
                    foreach (DataGridViewRow row in selectedRows)
                    {
                        var transaction = row.DataBoundItem as AddTransaction;
                        if (transaction != null)
                        {
                            transactions.Add(transaction);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                LogManager.GetLogger(Utilities.Logger).Error("Error getting selected entries", ex);
            }

            return transactions;
        }

        private void transactionDataGridView_MouseClick(object sender, MouseEventArgs e)
        {
            if ((e.Button == MouseButtons.Right) && (!backgroundWorker.IsBusy))
            {
                DataGridView.HitTestInfo hitTest = transactionDataGridView.HitTest(e.X, e.Y);
                if ((hitTest.Type == DataGridViewHitTestType.Cell) && (hitTest.RowIndex != -1))
                {
                    // Right-click on a cell, show the context menu
                    contextMenuStrip.Show(transactionDataGridView.PointToScreen(e.Location));
                }
            }
        }

        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            using (var about = new AboutDialog())
            {
                about.ShowDialog();
            }
        }

        private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            var transactions = e.Argument as AddTransactionList;
            if (transactions != null)
            {
                _presenter.Delete(transactions);
            }
        }

        private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            DeleteOperationComplete();
            UpdateButtonState();
        }

        private void transactionDataGridView_SelectionChanged(object sender, EventArgs e)
        {
            UpdateButtonState();
        }

        private void UpdateButtonState()
        {
            bool enabled = false;
            bool hasLocked = false;
            bool hasUnlocked = false;

            if (!backgroundWorker.IsBusy)
            {
                List<AddTransaction> transactions = GetSelectedEntries();
                if (transactions.Count > 0)
                {
                    enabled = true;

                    // Determine whether any selected items are locked and/or unlocked
                    foreach (AddTransaction transaction in transactions)
                    {
                        if (transaction.Locked)
                        {
                            hasLocked = true;
                        }
                        else
                        {
                            hasUnlocked = true;
                        }
                        
                        // Exit early if we can
                        if (hasLocked && hasUnlocked)
                        {
                            break;
                        }
                    }
                }
            }

            deleteTransactionToolStripMenuItem.Enabled = enabled;
            lockTransactionsToolStripMenuItem.Enabled = enabled && hasUnlocked;
            unlockTransactionsToolStripMenuItem.Enabled = enabled && hasLocked;
            buttonDelete.Enabled = enabled;
            buttonLock.Enabled = enabled && hasUnlocked;
            buttonUnlock.Enabled = enabled && hasLocked;
        }

        private void buttonRefresh_Click(object sender, EventArgs e)
        {
            _presenter.Refresh();
        }

        private void addTransactionBindingSource_DataSourceChanged(object sender, EventArgs e)
        {
            buttonRefresh.Enabled = (addTransactionBindingSource.DataSource != null);
        }

        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            OnSaveSettings(EventArgs.Empty);
            Properties.Settings.Default.Save();
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            _presenter.Initialize();
        }

        private delegate void UpdateMRUDel(IList<string> mruItems);

        private void UpdateMRU(IList<string> mruItems)
        {
            if ((mruItems == null) || (mruItems.Count == 0))
            {
                return;
            }

            if (InvokeRequired)
            {
                BeginInvoke(new UpdateMRUDel(UpdateMRU), new object[] { mruItems });
            }
            else
            {
                for (int count = 0; count < mruItems.Count; count++)
                {
                    var mruItem = new ToolStripMenuItem(String.Format("{0}. {1}", count+1, mruItems[count]));
                    mruItem.Click += OnMruClick;
                    mruItem.Tag = mruItems[count];

                    openRecentToolStripMenuItem.DropDownItems.Add(mruItem);
                }

                // Make the parent menu visible if any children items exist and are visible
                openRecentToolStripMenuItem.Visible = ((openRecentToolStripMenuItem.DropDownItems != null) && (openRecentToolStripMenuItem.DropDownItems.Count > 0));
            }
        }

        void OnMruClick(object sender, EventArgs e)
        {
            var item = sender as ToolStripMenuItem;
            if (item != null)
            {
                string location = item.Tag as string;
                if (!String.IsNullOrEmpty(location))
                {
                    _presenter.SymbolStoreLocation = location;
                }
                else
                {
                    LogManager.GetLogger(Utilities.Logger).WarnFormat("MRU Item did not have an associated symbol store location: {0}", item.Text);
                }
            }
        }

        private delegate void SetDataSourceDel(SortableBindingList<AddTransaction> transactions);

        private void SetDataSource(SortableBindingList<AddTransaction> transactions)
        {
            if (InvokeRequired)
            {
                BeginInvoke(new SetDataSourceDel(SetDataSource), new object[] { transactions });
            }
            else
            {
                using (new Hourglass())
                {
                    addTransactionBindingSource.DataSource = transactions;
                }
            }
        }

        #region ITransactionManagerView Members

        public event EventHandler SaveSettings;

        protected virtual void OnSaveSettings(EventArgs e)
        {
            try
            {
                EventHandler evt = SaveSettings;
                if (evt != null)
                {
                    evt(this, e);
                }
            }
            catch
            { }
        }

        public IList<string> SymbolStoreMRU
        {
            set
            {
                UpdateMRU(value);
            }
        }

        public void ClearOutput()
        {
            BeginInvoke((MethodInvoker) (() => outputRTB.Text = String.Empty));
        }

        private delegate void WriteOutputDel(string output, OutputMessageType messageType);

        public void WriteOutput(string output, OutputMessageType messageType)
        {
            if (String.IsNullOrEmpty(output))
            {
                return;
            }

            if (InvokeRequired)
            {
                BeginInvoke(new WriteOutputDel(WriteOutput), new object[] { output, messageType });
            }
            else
            {
                outputRTB.SelectionColor = GetColor(messageType);
                outputRTB.AppendText(String.Format("{0}\n", output));
                outputRTB.SelectionColor = outputRTB.ForeColor;
                outputRTB.ScrollToCaret();
            }
        }

        private static Color GetColor(OutputMessageType messageType)
        {
            switch (messageType)
            {
                case OutputMessageType.Debug:
                    return Color.DarkGray;
                case OutputMessageType.Error:
                    return Color.Red;
                case OutputMessageType.Normal:
                    return Color.Black;
                case OutputMessageType.Verbose:
                    return Color.Blue;
                case OutputMessageType.Warning:
                    return Color.Yellow;
            }

            return Color.DarkRed;
        }

        private delegate void ReportProgressDel(bool showProgress, int progressPercentage, string operation, string description);

        public void ReportProgress(bool showProgress, int progressPercentage, string operation, string description)
        {
            if (InvokeRequired)
            {
                BeginInvoke(new ReportProgressDel(ReportProgress), new object[] { showProgress, progressPercentage, operation, description });
            }
            else
            {
                toolStripProgressBar.Visible = showProgress;
                toolStripProgressBar.Value = progressPercentage;
            }
        }

        private delegate int PromptDel(string caption, string message, Collection<ChoiceDescription> choices, int defaultChoice);

        public int Prompt(string caption, string message, Collection<ChoiceDescription> choices, int defaultChoice)
        {
            if (InvokeRequired)
            {
                return (int)Invoke(new PromptDel(Prompt), new object[] { caption, message, choices, defaultChoice });
            }
            
            int returnValue = defaultChoice;

            if ((choices != null) && (choices.Count > 0))
            {
                var p = new DynamicPrompt
                            {
                                Caption = caption,
                                Message = message
                            };
                p.SetChoices(choices, 0);

                returnValue = p.SelectedChoice;
            }

            return returnValue;
        }

        public string Status
        {
            set
            {
                // No-op (for now)
            }
        }

        public SortableBindingList<AddTransaction> Transactions
        {
            set
            {
                SetDataSource(value);
            }
        }

        public string SymbolStoreLocation
        {
            set
            {
                SetSymbolStoreLocation(value);
            }
        }

        private void SetSymbolStoreLocation(string value)
        {
            if (String.IsNullOrEmpty(value))
            {
                statusServerLocation.Text = "[Press CTRL+O to specify a location]";
            }
            else
            {
                statusServerLocation.Text = value;
            }
        }

        #endregion

        private void transactionDataGridView_DataError(object sender, DataGridViewDataErrorEventArgs e)
        {
            if (e.Exception != null)
            {
                LogManager.GetLogger(Utilities.Logger).Error("DataGridView error", e.Exception);
            }
        }

        private void transactionDataGridView_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
        {
            if ((e.ColumnIndex == Locked.Index) 
                && (e.DesiredType == typeof(Image))
                && (e.Value is bool))
            {
                bool locked = (bool)e.Value;
                e.Value = (locked) ? _lockIcon : _blankIcon;
                e.FormattingApplied = true;
            }
        }
    }
}